package client.dubbo.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.apache.log4j.Logger;

import client.dubbo.DubboClientController;
import client.dubbo.DubboTreeNode;
import client.dubbo.IDubboClient;
import client.dubbo.impl.DO.DubboMethodTreeNode;
import client.dubbo.impl.DO.DubboRootTreeNode;
import client.dubbo.impl.DO.DubboServiceTreeNode;
import client.redis.RedisTreeNode;
import client.redis.impl.DO.RedisKeyTreeNode;
import core.AlertUtil;
import core.FormDialog;
import core.InteractSocket;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;

/**
 * 单台Dubbo连接客户端
 * @author self
 *
 */
public class DubboClientImpl implements IDubboClient {

	//客户端名称
	private static final String CLIENT_NAME = "Dubbo客户端(单机)";
	
	//应用日志对象
	private Logger appLogger;
	
	//界面控制器组件对象
	private DubboClientController controller;
		
	//交互式Socket
	private InteractSocket socket;
	
	//连接信息
	private ConnectInfo connectInfo;
	
	@Override
	public String getName() {
		return CLIENT_NAME;
	}

	@Override
	public void init(DubboClientController controller) {
		this.controller = controller;
		this.appLogger = controller.getAppLogger();
		appLogger.info("初始化客户端组件！");
		createConnection();
	}
	
	/**
	 * 创建连接
	 * @return
	 */
	private boolean createConnection() {
		appLogger.info("创建Socket连接！");
		TextField hostTextField = new TextField("127.0.0.1");
		TextField portTextField = new TextField("20880");
		FormDialog newConnForm = new FormDialog("新建链接"
				,new FormDialog.Item("主机：", hostTextField)
				,new FormDialog.Item("端口：", portTextField));
		Optional<Boolean> result = newConnForm.showAndWait();
		if(result.isPresent()) {
			String host = hostTextField.getText();
			int port = Integer.valueOf(portTextField.getText());
			appLogger.info("Socket连接参数 -> host:"+host+",port:"+port);
			this.connectInfo = new ConnectInfo(host, port);
			try {
				socket = new InteractSocket(host, port);
				this.controller.getCmdTextArea().setText("已连接！\n\ndubbo>");
				appLogger.info("已连接到Dubbo服务器。");
				initTreeView();
			} catch (IOException e) {
				AlertUtil.exception("连接失败", e);
			}
		}
		return false;
	}

	/**
	 * 初始化TreeView树
	 */
	private void initTreeView() {
		appLogger.info("初始化Dubbo树结构。");
		TreeView<DubboTreeNode> dubboTreeView = controller.getDubboTreeView();
		appLogger.info("初始化树节点！");
		TreeItem<DubboTreeNode> root = this.getRootTreeItem();
		dubboTreeView.setRoot(root);
		
		appLogger.info("添加节点选择监听事件！");
		//节点选择事件处理
		dubboTreeView.getSelectionModel().selectedItemProperty().addListener((observable,oldValue,newValue)->{
			appLogger.info("Dubbo树节点被点击 -> oldValue:"+oldValue+",newValue:"+newValue);
			TreeItem<DubboTreeNode> currentSelectedItem = (TreeItem<DubboTreeNode>) newValue;
			if(currentSelectedItem==null) return;
			
			//设置节点信息
			DubboTreeNode selectedNode = currentSelectedItem.getValue();
			String itemInfo = this.getItemInfo(selectedNode);
			controller.getInfoTextArea().setText(itemInfo);
			
			if(!selectedNode.isLeaf() && !selectedNode.isLoaded()) {
				currentSelectedItem.getChildren().clear();
				List<TreeItem<DubboTreeNode>> childNodeList = this.getChildren(selectedNode);
				currentSelectedItem.getChildren().addAll(childNodeList);
				currentSelectedItem.setExpanded(true);
			}
			
			selectedNode.setLoaded(true);
		});
		
		appLogger.info("设置树节点右键菜单！");
		//设置TreeView树邮件菜单
		MenuItem refreshItem = new MenuItem("刷新");
		refreshItem.setOnAction((event)->{
			appLogger.info("执行[刷新]动作！");
			TreeItem<DubboTreeNode> selectedItem = dubboTreeView.getSelectionModel().getSelectedItem();
			DubboTreeNode dubboTreeNode = selectedItem.getValue();
			//刷新信息
			this.controller.getInfoTextArea().setText(getItemInfo(dubboTreeNode));

			//刷新子节点
			if(!dubboTreeNode.isLeaf()) {
				selectedItem.getChildren().clear();
				selectedItem.getChildren().addAll(this.getChildren(dubboTreeNode));
				selectedItem.setExpanded(true);
			}
		});
		
		MenuItem disconnectItem = new MenuItem("断开连接");
		disconnectItem.setOnAction((event)->{
			appLogger.info("执行[断开连接]动作！");
			try {
				this.close();
				controller.getDubboTreeView().setRoot(null);
				controller.getDubboTreeView().setContextMenu(null);
				controller.getInfoTextArea().clear();
				controller.getCmdTextArea().setText("连接已断开！\n");
			} catch (IOException e) {
				AlertUtil.exception(e);
			}
		});
		
		MenuItem invokeItem = new MenuItem("调用方法");
		invokeItem.setOnAction((event)->{
			appLogger.info("执行[调用方法]动作！");
			TreeItem<DubboTreeNode> selectedItem = dubboTreeView.getSelectionModel().getSelectedItem();
			DubboTreeNode dubboTreeNode = selectedItem.getValue();
			if(dubboTreeNode instanceof DubboMethodTreeNode) {
				String service = ((DubboMethodTreeNode) dubboTreeNode).getService();
				String method = ((DubboMethodTreeNode) dubboTreeNode).getMethod();
				Optional<String> result = AlertUtil.input("调用方法","请输入方法参数：");
				if(result.isPresent()) {
					String params = result.get();
					String data = this.exeCmd("invoke "+service+"."+method+"("+params+")");
					AlertUtil.info("调用结果",data);
				}
			}
		});
		
		dubboTreeView.setContextMenu(new ContextMenu(disconnectItem)); 
		dubboTreeView.setOnContextMenuRequested((event)->{
			dubboTreeView.getContextMenu().getItems().clear();
			TreeItem<DubboTreeNode> selectedItem = dubboTreeView.getSelectionModel().getSelectedItem();
			DubboTreeNode DubboTreeNode = selectedItem.getValue();
			if(DubboTreeNode instanceof DubboRootTreeNode) {
				dubboTreeView.getContextMenu().getItems().add(disconnectItem);
			}else if(DubboTreeNode instanceof DubboMethodTreeNode) {
				dubboTreeView.getContextMenu().getItems().add(invokeItem);
			}
		});
		
		appLogger.info("Dubbo树结构初始化完成！");
	}
	
	/**
	 * 获取根节点
	 * @return　根节点对象
	 */
	private TreeItem<DubboTreeNode> getRootTreeItem() {
		appLogger.info("获取Dubbo树根节点！");
		DubboRootTreeNode rootNode = new DubboRootTreeNode(connectInfo.getHost(),connectInfo.getPort());
		TreeItem<DubboTreeNode> rootItem = new TreeItem<DubboTreeNode>(rootNode);
		rootItem.setExpanded(true);
		rootItem.getChildren().addAll(this.getChildren(rootNode));
		return rootItem;
	}
	
	/**
	 * 获取子节点集合
	 * @param selectedNode　当前选中节点
	 * @return　子节点集合
	 */
	private List<TreeItem<DubboTreeNode>> getChildren(DubboTreeNode selectedNode) {
		appLogger.info("获取Dubbo树子节点！当前节点："+selectedNode);
		if(selectedNode instanceof DubboRootTreeNode) {
			List<TreeItem<DubboTreeNode>> childrenList = new ArrayList<TreeItem<DubboTreeNode>>();
			String result = this.exeCmd("ls");
			String[] lines = result.split("\n");
			for(int i=0;i<lines.length;i++) {
				if(lines[i]==null||"".equals(lines[i].trim())) {
					continue;
				}
				if(lines[i].startsWith("PROVIDER:")||lines[i].startsWith("dubbo>")) {
					continue;
				}
				String service = lines[i].trim();
				DubboServiceTreeNode serviceTreeNode = new DubboServiceTreeNode(service);
				TreeItem<DubboTreeNode> serviceItem = new TreeItem<DubboTreeNode>(serviceTreeNode);
				childrenList.add(serviceItem);
			}
			return childrenList;
		}else if(selectedNode instanceof DubboServiceTreeNode) {
			String service = ((DubboServiceTreeNode) selectedNode).getService();
			List<TreeItem<DubboTreeNode>> childrenList = new ArrayList<TreeItem<DubboTreeNode>>();
			String result = this.exeCmd("ls "+service);
			String[] lines = result.split("\n");
			for(int i=0;i<lines.length;i++) {
				if(lines[i]==null||"".equals(lines[i].trim())) {
					continue;
				}
				if(lines[i].startsWith(service)||lines[i].startsWith("dubbo>")) {
					continue;
				}
				String method = lines[i].trim();
				DubboMethodTreeNode methodTreeNode = new DubboMethodTreeNode(service,method);
				TreeItem<DubboTreeNode> methodItem = new TreeItem<DubboTreeNode>(methodTreeNode);
				childrenList.add(methodItem);
			}
			return childrenList;
		}
		return null;
	}

	/**
	 * 获取节点信息
	 * @param selectedNode　当前选中节点
	 * @return　节点信息
	 */
	private String getItemInfo(DubboTreeNode selectedNode) {
		appLogger.info("获取Dubbo树节点详情信息。当前节点："+selectedNode);
		if(selectedNode instanceof DubboRootTreeNode) {
			return removeCmdTip(this.exeCmd("status -l"));
		}else if(selectedNode instanceof DubboServiceTreeNode) {
			String service = ((DubboServiceTreeNode) selectedNode).getService();
			return removeCmdTip(this.exeCmd("count "+service));
		}else if(selectedNode instanceof DubboMethodTreeNode) {
			String service = ((DubboMethodTreeNode) selectedNode).getService();
			String method = ((DubboMethodTreeNode) selectedNode).getMethod();
			return removeCmdTip(this.exeCmd("count "+service+" "+method));
		}
		return null;
	}
	
	/**
	 * 删除命令提示部分
	 * @param str　待处理字符串
	 * @return　去除命令提示部分后的字符串
	 */
	private String removeCmdTip(String str) {
		if(str == null || "".equals(str)) {
			return "";
		}
		return str.replaceAll("dubbo>","").replaceAll("telnet>","");
	}
	
	@Override
	public String exeCmd(String cmd) {
		if(cmd==null||"".equals(cmd.trim())) {
			return null;
		}
		try {
			appLogger.info("执行命令："+cmd);
			//命令如果没有换行符则补充一个
			if(!cmd.endsWith("\n")) {
				cmd+="\n";
			}
			
			String result=socket.sendCommand(cmd);
			appLogger.info("执行结果："+result);
			return result;
		} catch (IOException e) {
			AlertUtil.exception(e);
		}
		return null;
	}

	@Override
	public void close() throws IOException {
		if(socket!=null) {
			socket.close();
		}
	}
	
	private static class ConnectInfo{
		private String host;
		private int port;
		
		public ConnectInfo(String host,int port) {
			this.host = host;
			this.port =port;
		}
		
		public String getHost() {
			return host;
		}
		
		public int getPort() {
			return port;
		}
	}
}
